package main

import (
	"fmt"
	"image/color"
	"math"
	"sync"
)

type Point struct { x, y float64 }

func Distance(p, q Point) float64 {
	return math.Hypot(p.x - q.x , p.y - q.y)
}

func (p Point) Distance(q Point) float64 {
	return math.Hypot(q.x - p.x, q.y - p.y)
}

func (p *Point) ScaleBy(factor float64) {
	p.x *= factor
	p.y *= factor
}

func t_gemometry() {
	p := Point{1,2}
	q := Point{4,6}
	fmt.Println("pure func:" , Distance(p, q))
	fmt.Println("method call: ", p.Distance(q))
}

////////////////////
type Path []Point

// 不同类型的同名方法
func (path Path) Distance() float64 {
	sum := 0.0
	for i := range path {
		if i > 0 {
			sum += path[i-1].Distance(path[i])
		}
	}
	return sum
}

func T_total_path()  {
	perim := Path {
		{1, 1},
		{5,1},
		{5,4},
		{1,1},
	}
	fmt.Println("same method name of different type:", perim.Distance())
}

func t_pointer_method_call() {
	// receiver argument是type T，receiver parameter 也是type T
	r := &Point{1,2}
	r.ScaleBy((2))

	// r2被编译器隐式转换成&Point
	// receiver argument 是type T，parameter是type *T，被编译器隐式转换
	r2 := Point{1,2}
	r2.ScaleBy((2))

	//Point{1,2}.ScaleBy(2) //不能从临时变量取地址，所以这样不行

	//可以从指针变量调用named type的方法，因为可以从指针得到值
	// receiver argument是type *T，可以调用type T方法
	r.Distance(Point{3,4}) // 编译器转为:(*r).Distance
}

/////////////////////////////////// 6.3 组合匿名field到复合类型(composing type by struct embedding)
type ColoredPoint struct {
	Point
	color color.RGBA
}

// 也可以把匿名指针放入复合类型中
type ColoredPointer struct {
	*Point
	Color color.RGBA
}

func t_struct_embedding() {
	var cp ColoredPoint
	cp.x = 1
	fmt.Println(cp.Point.x)

	cp.Point.y = 2
	fmt.Println(cp.y)

	red := color.RGBA{255, 0, 0, 255}
	blue := color.RGBA{0,0,255,255}
	var p = ColoredPoint{Point{1,2}, red}
	var q = ColoredPoint{Point{3,4}, blue}

	// 可以直接调用匿名field的方法，就好像是自己定义的一样
	fmt.Println(p.Distance(q.Point))
	q.ScaleBy(2)

	// 复合类型(匿名指针版本)
	p1 := ColoredPointer{&Point{1,2}, red}
	q1 := ColoredPointer{&Point{1,1}, blue}
	fmt.Println(p1.Distance(*q1.Point))
	q1.Point = p1.Point // p1 and q1 share the same Point
	p1.ScaleBy(2)
}

// 6.4 method values and method expressions

func t_method_values_and_method_expressions()  {
	p := Point{1,2}
	q := Point{3,4}
	distanceFromP := p.Distance // 它是method value，可以传给函数参数
	fmt.Println(distanceFromP(q))

	scaleP := p.ScaleBy
	scaleP(2)

	distance := Point.Distance
	fmt.Println(distance(p, q)) // 这是method expressions,第一个参数作为receiver
	scale := (*Point).ScaleBy
	scale(&p, 2)

}

// 有了匿名类型组合，匿名struct也可以有方法了
func t_cache()  {
	var cache = struct {
		sync.Mutex
		mapping map[string]string
	}{
		mapping: make(map[string]string),
	}

	lookup := func(k string) string {
		cache.Lock() //这样的语法更易懂些 self-explanatory syntax
		v := cache.mapping[k]
		cache.Unlock()
		return v
	}
	fmt.Println(lookup)
}

func main() {
	t_struct_embedding()
	//T_method_values_and_method_expressions()
	//T_gemometry()
	//T_total_path()
}